{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Day16 再谈正则中的分组"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　在应用正则表达式的过程中，我们经常会遇到下面这样的问题："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-15T12:11:56.244688Z",
     "iopub.status.busy": "2020-09-15T12:11:56.243692Z",
     "iopub.status.idle": "2020-09-15T12:11:56.254663Z",
     "shell.execute_reply": "2020-09-15T12:11:56.253665Z",
     "shell.execute_reply.started": "2020-09-15T12:11:56.243692Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('lng', '106.323233'), ('lat', '29.989878'), ('altitude', '-9.9')]"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import re\n",
    "\n",
    "s = '''{\"lng\": 106.323233, \n",
    "        \"lat\": 29.989878,\n",
    "        \"altitude\": -9.9}'''\n",
    "\n",
    "# 找出所有的键值对\n",
    "re.findall('\"(.*?)\": (.*?)[,\\}]', s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　在前面的日程中对于这个情况我有简短地提到过，这其实就是正则中的**分组**，今天的日程我们就来正式的理解它。\n",
    "  \n",
    "　　顾名思义，**分组**体现在我们在书写正则表达式时使用到两对及以上的`()`，就像上面的例子一样，我们感兴趣的是字典中的键值对，而**键**与**值**在匹配时是一套模式下的不同位置对应字符片段，因此像上面的例子一样结果是成对的。\n",
    "  \n",
    "　　但如果是下面这种情况："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-15T12:11:56.255662Z",
     "iopub.status.busy": "2020-09-15T12:11:56.255662Z",
     "iopub.status.idle": "2020-09-15T12:11:56.259650Z",
     "shell.execute_reply": "2020-09-15T12:11:56.259650Z",
     "shell.execute_reply.started": "2020-09-15T12:11:56.255662Z"
    }
   },
   "outputs": [],
   "source": [
    "# 匹配连续出现的abc\n",
    "s = 'abcabc abcabcabc abc abcabcabcabc'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　如果你想当然的像下面这样写："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-15T12:11:56.261646Z",
     "iopub.status.busy": "2020-09-15T12:11:56.260648Z",
     "iopub.status.idle": "2020-09-15T12:11:56.267630Z",
     "shell.execute_reply": "2020-09-15T12:11:56.266631Z",
     "shell.execute_reply.started": "2020-09-15T12:11:56.261646Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['abc', 'abc', 'abc', 'abc']"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "re.findall('(abc)+', s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　这是因为我们这里的`()`划定出的分组匹配模式依旧是内部包裹的`abc`，而后面紧跟的`+`虽然配合`(abc)`的确表示我们想要的模式，但因为没有继续被`()`包裹，所以只是匹配到且消耗掉，但并不会作为返回结果返回，我们继续嵌套`()`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-15T12:11:56.268627Z",
     "iopub.status.busy": "2020-09-15T12:11:56.268627Z",
     "iopub.status.idle": "2020-09-15T12:11:56.275607Z",
     "shell.execute_reply": "2020-09-15T12:11:56.274611Z",
     "shell.execute_reply.started": "2020-09-15T12:11:56.268627Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('abcabc', 'abc'),\n",
       " ('abcabcabc', 'abc'),\n",
       " ('abc', 'abc'),\n",
       " ('abcabcabcabc', 'abc')]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "re.findall('((abc)+)', s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "　　这个时候我们会发现返回的结果列表中每个元素都是个2元组，其中每个2元组第一个元素是我们想要的内容，而第二个元素则跟之前遇到的情况一致，这正是正则的分组在**搞鬼**。\n",
    "  \n",
    "　　对于像上例所示存在嵌套分组的情况时，从左往右数所有成对小括号中的左括号，依次就代表第1组、第2组...第n组，下面我们来看一个更形象的例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-15T12:11:56.276604Z",
     "iopub.status.busy": "2020-09-15T12:11:56.275607Z",
     "iopub.status.idle": "2020-09-15T12:11:56.283589Z",
     "shell.execute_reply": "2020-09-15T12:11:56.282588Z",
     "shell.execute_reply.started": "2020-09-15T12:11:56.276604Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('abcdef', 'abcde', 'abcd', 'abc', 'ab', 'a'),\n",
       " ('abcdef', 'abcde', 'abcd', 'abc', 'ab', 'a'),\n",
       " ('abcdef', 'abcde', 'abcd', 'abc', 'ab', 'a')]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = 'abcdef abcdef abcdef'\n",
    "\n",
    "re.findall('((((((a)b)c)d)e)f)', s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Day16 课后小测验\n",
    "\n",
    "　　在学习完今天的日程并对正则中的分组理解了之后，请你从下面的字符串中利用分组的知识提取出每个格式如`(市全称, 市简称, 区全程, 区简称)`，譬如`('重庆市', '重庆', '渝中区', '渝中')`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-09-15T12:11:56.285581Z",
     "iopub.status.busy": "2020-09-15T12:11:56.285581Z",
     "iopub.status.idle": "2020-09-15T12:11:56.289570Z",
     "shell.execute_reply": "2020-09-15T12:11:56.289570Z",
     "shell.execute_reply.started": "2020-09-15T12:11:56.285581Z"
    }
   },
   "outputs": [],
   "source": [
    "target = '重庆市渝中区 北京市西城区 成都市武侯区'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-08-26T12:55:19.898241Z",
     "iopub.status.busy": "2020-08-26T12:55:19.897245Z",
     "iopub.status.idle": "2020-08-26T12:55:19.904225Z",
     "shell.execute_reply": "2020-08-26T12:55:19.903227Z",
     "shell.execute_reply.started": "2020-08-26T12:55:19.898241Z"
    }
   },
   "source": [
    "　　请将你的答案截图发到本帖评论区~"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('重庆市', '重庆', '渝中区', '渝中'),\n",
       " ('北京市', '北京', '西城区', '西城'),\n",
       " ('成都市', '成都', '武侯区', '武侯')]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "re.findall('((\\w+?)市)((\\w+?)区)',target)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
